#ifndef __TDP2_FPSYSTEM_HPP__
#define __TDP2_FPSYSTEM_HPP__

#include "../../rdx/src/rdx/rdx_threading.hpp"
#include "../../rdx/src/rdx/rdx_io.hpp"
#include "tdp2_hashmap.hpp"
#include "tdp2_allocator.hpp"
#include "tdp2_string.hpp"
#include "tdp2_guard.hpp"
#include "tdp2_zipfile.hpp"
#include "../zlib/zlib.h"

namespace RDX
{
	namespace IO
	{
		struct IFileStream;
		struct IFileSystem;
	}
}

namespace TDP
{
	class PanicHandler;

	namespace ResourceManagement
	{
		class MFPArchive
		{
			Containers::HashMap<CString, LargeUInt> _filesByName;
			Containers::Vector<ZipFile::ArchiveEntry> _archiveFiles;
			RDX::ObjectManagement::Allocator _alloc;
			DynString<Char> _archivePath;
			RDX::IO::IFileSystem *_baseFS;
			bool _loaded;
			bool _initialized;
			
		public:
			MFPArchive(const RDX::ObjectManagement::Allocator &alloc, RDX::IO::IFileSystem *baseFS, const Char *path);		// WARNING: This can potentially fail, check InitializedOK
			void InitializeArchive(RDX::IO::IFileStream *arcF);
			RDX::IO::IFileStream *OpenFileRead(const Char *path) const;

			inline bool LoadedOK() const
			{
				return _loaded;
			}

			inline bool InitializedOK() const
			{
				return _initialized;
			}
		};

		class MFPResourceManager
		{
			Containers::HashMap<DynString<Char>, Containers::SingleInstance<MFPArchive>> _archives;
			RDX::Threading::LightMutex _mutex;
			RDX::ObjectManagement::Allocator _alloc;
			RDX::IO::IFileSystem *_baseFS;
			
			RDX::IO::IFileStream *OpenFileMFP(const Char *wpath);
			RDX::IO::IFileStream *OpenFileSFP(const Char *wpath);
			
		public:
			MFPResourceManager(RDX::IO::IFileSystem *fs, const RDX::ObjectManagement::Allocator &alloc = TDP::Memory::DefaultAllocator());
			RDX::IO::IFileStream *OpenFileRead(const Char *path, bool sfp);
		};

		class MFPDataStream : public RDX::IO::IFileStream
		{
			static const LargeUInt DecompressedDataSize_k = 100000;
			static const LargeUInt CompressedDataSize_k = 32768;

			bool _isCompressed;
			LargeUInt _compressedSize;
			LargeUInt _uncompressedSize;

			LargeUInt _dataStartLocation;
			LargeUInt _tellOffset;
			LargeUInt _compressedDataConsumed;

			z_stream _stream;
			bool _streamActive;
			bool _streamEnded;	// Can't read any more data even if there should be more
			bool _hasAborted;
			RDX::IO::IFileStream *_f;
			LargeInt _dOffset;
			LargeInt _dSize;
			RDX::ObjectManagement::Allocator _alloc;
			UInt8 _decompressedData[DecompressedDataSize_k];
			UInt8 _compressedData[CompressedDataSize_k];

			static void *zalloc(void *opaque, unsigned int items, unsigned int size);
			static void zfree(void *opaque, void *address);

			bool RestartDecompression();
			LargeUInt ConsumeDecompressedData(LargeUInt maxBytes);

		public:
			explicit MFPDataStream(const RDX::ObjectManagement::Allocator &alloc);
			~MFPDataStream();

			bool OpenInlineFile(RDX::IO::IFileStream *f, LargeUInt compressedSize, LargeUInt uncompressedSize, bool isCompressed);

			LargeInt WriteBytes(const void *src, LargeInt numBytes);

			LargeInt ReadBytes(void *dest, LargeInt numBytes);
			void Seek(LargeInt offset, SeekType seekType);
			LargeInt Tell();
			void Close();
			void Abort();
			bool HasAborted() const;
		};

		class MFPFileSystem : public RDX::IO::IFileSystem
		{
			RDX::IO::IFileSystem *_parentSystem;
			MFPResourceManager *_mfpRM;

		public:
			MFPFileSystem(IFileSystem *parentSystem, MFPResourceManager *mfpRM);

			virtual RDX::IO::IFileStream *Open(const Char *path, bool write);
			virtual RDX::IO::IFileScanState *ScanDirectory(const Char *directory, const Char *wildcard, const Char *vext);
		};

		class MFPScanState : public RDX::IO::IFileScanState
		{
		public:
			virtual void Close();
			virtual RDX::ObjectManagement::CRef<const RDX::Programmability::String> NextFile(RDX::OperationContext *ctx, RDX::ObjectManagement::IObjectManager *objm);
		};
	}
}

#endif
